use poem::{
    async_trait,
    http::StatusCode,
    web::headers::{authorization::Bearer, Authorization, HeaderMapExt},
    Endpoint, Error, IntoResponse, Middleware, Request, Response, Result,
};

pub struct Authenticate;
pub struct AuthenticateEndpoint<E>(E);

impl<E: Endpoint> Middleware<E> for Authenticate {
    type Output = AuthenticateEndpoint<E>;

    fn transform(&self, ep: E) -> Self::Output {
        AuthenticateEndpoint(ep)
    }
}

#[async_trait]
impl<E: Endpoint> Endpoint for AuthenticateEndpoint<E> {
    type Output = Response;

    async fn call(&self, mut req: Request) -> Result<Self::Output> {
        match req
            .headers()
            .typed_get::<Authorization<Bearer>>()
            .and_then(|Authorization(bearer)| codec::parse(bearer.token()).ok())
        {
            None => Err(Error::from_status(StatusCode::UNAUTHORIZED)),
            Some(my_claims) => {
                req.set_data(my_claims.username);

                // 有 await ，不能使用 Monad 操作
                self.0.call(req).await.map(IntoResponse::into_response)
            }
        }
    }
}
